package com.lrabbit.utils;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.util.*;

@Slf4j
@Component
public class BeanUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    /**
     * 实现ApplicationContextAware接口的context注入函数.
     *
     * @param arg0 ApplicationContext
     * @throws BeansException 如果在获取Bean时发生任何异常
     */
    @Override
    public void setApplicationContext(ApplicationContext arg0) throws BeansException {
        applicationContext = arg0;
    }

    /**
     * 根据bean的id从applicationContext中获取bean对象
     *
     * @param id bean的id
     * @return bean对象，如果找不到或者发生其他异常，则返回null
     */
    public static Object getBean(String id) {
        Object object = null;
        try {
            object = applicationContext.getBean(id);
        } catch (NoSuchBeanDefinitionException e) {
            // 找不到bean
            log.error("找不到bean: {}", id, e);
        } catch (BeansException e) {
            // 其他异常
            log.error("获取bean发生异常: {}", id, e);
        }
        return object;
    }

    /**
     * 获取空属性
     *
     * @param source 源数据
     * @return 空属性字符串数组
     */
    public static String[] getNullPropertyNames(Object source) {
        final BeanWrapper src = new BeanWrapperImpl(source);
        PropertyDescriptor[] pds = src.getPropertyDescriptors();

        Set<String> emptyNames = new HashSet<>();
        for (PropertyDescriptor pd : pds) {
            Object srcValue = src.getPropertyValue(pd.getName());
            if (srcValue == null) {
                emptyNames.add(pd.getName());
            }
        }
        String[] result = new String[emptyNames.size()];
        return emptyNames.toArray(result);
    }

    /**
     * 拷贝对象
     *
     * @param source 源对象
     * @param target 目标对象
     */
    public static void copyPropertiesIgnoreNull(Object source, Object target) {
        BeanUtils.copyProperties(source, target, getNullPropertyNames(source));
    }

    /**
     * 对象拷贝
     *
     * @param object  源对象
     * @param voClass 目标类
     * @return 目标对象的副本
     */
    public static Object copyObject(Object object, Class<?> voClass) {
        if (object == null) {
            return null;
        }

        Object voObject = null;
        try {
            Constructor<?> constructor = voClass.getDeclaredConstructor();
            constructor.setAccessible(true);
            voObject = constructor.newInstance();
        } catch (NoSuchMethodException | InstantiationException | InvocationTargetException |
                 IllegalAccessException e) {
            log.error("对象拷贝异常", e);
        }

        if (voObject != null) {
            BeanUtils.copyProperties(object, voObject);
        }
        return voObject;
    }

    /**
     * List 对象拷贝
     *
     * @param poList  源list
     * @param voClass 目标类
     * @return 目标list的副本
     */
    public static List<Object> copyToList(List<?> poList, Class<?> voClass) {
        List<Object> voList = new ArrayList<>();
        Object voObject = null;
        if (poList == null) {
            return null;
        }

        for (Object poObject : poList) {
            try {
                Constructor<?> constructor = voClass.getDeclaredConstructor();
                constructor.setAccessible(true);
                voObject = constructor.newInstance();
            } catch (NoSuchMethodException | InstantiationException | InvocationTargetException |
                     IllegalAccessException e) {
                log.error("List对象拷贝异常", e);
            }
            if (voObject != null) {
                BeanUtils.copyProperties(poObject, voObject);
                voList.add(voObject);
            }
        }
        return voList;
    }

    /**
     * Object转map
     *
     * @param object 源对象
     * @return map对象
     */
    public static Map<String, Object> beanToMapWithJsonProperty(Object object) {
        return beanToMap(object);
    }

    /**
     * Object转map
     *
     * @param object 源对象
     * @param prefix 前缀
     * @return map对象
     */
    public static Map<String, Object> beanToMapWithJsonPropertyPrefix(Object object, String prefix) {
        Map<String, Object> map = new HashMap<>();
        try {
            Field[] fields = object.getClass().getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                JsonProperty annotation = field.getAnnotation(JsonProperty.class);
                if (annotation != null && StringUtils.isNotBlank(annotation.value())) {
                    map.put(prefix + annotation.value(), field.get(object));
                } else {
                    map.put(prefix + field.getName(), field.get(object));
                }
            }

        } catch (IllegalAccessException e) {
            log.error("object转map异常: {}", e.getMessage());
        }
        return map;
    }

    /**
     * map转object
     *
     * @param object 源对象
     * @return map对象
     */
    public static Map<String, Object> beanToMap(Object object) {
        Map<String, Object> map = new HashMap<>();
        try {
            Field[] fields = object.getClass().getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                JsonProperty annotation = field.getAnnotation(JsonProperty.class);
                if (annotation != null && StringUtils.isNotBlank(annotation.value())) {
                    map.put(annotation.value(), field.get(object));
                } else {
                    map.put(field.getName(), field.get(object));
                }
            }
        } catch (IllegalAccessException e) {
            log.error("object转map异常: {}", e.getMessage());
        }
        return map;
    }

    /**
     * map转object
     *
     * @param map   map对象
     * @param clazz 目标对象class
     * @param <T>   泛型类
     * @return 目标对象
     */
    public static <T> T mapToBean(Map map, Class<T> clazz) {
        try {
            T object = clazz.getDeclaredConstructor().newInstance();
            Field[] fields = object.getClass().getDeclaredFields();
            for (Field field : fields) {
                int mod = field.getModifiers();
                if (Modifier.isStatic(mod) || Modifier.isFinal(mod)) {
                    continue;
                }
                field.setAccessible(true);
                if (map.containsKey(field.getName())) {
                    field.set(object, map.get(field.getName()));
                }
            }
            return object;
        } catch (Exception e) {
            log.error("map转object异常: {}", e.getMessage());
        }
        return null;
    }

    /**
     * map转object
     *
     * @param map       map对象
     * @param beanClass 目标对象class
     * @param <T>       泛型
     * @return 目标对象
     */
    public static <T> T mapToBeanWithConverter(Map map, Class<T> beanClass) {
        try {
            T object = beanClass.newInstance();
            Field[] fields = object.getClass().getDeclaredFields();
            for (Field field : fields) {
                int mod = field.getModifiers();
                if (Modifier.isStatic(mod) || Modifier.isFinal(mod)) {
                    continue;
                }
                field.setAccessible(true);
                if (map.containsKey(field.getName())) {
                    if (null == map.get(field.getName())) {
                        continue;
                    }
                    if (field.getType() == Date.class) {
                        String str = String.valueOf(map.get(field.getName()));
                        Date date = DateUtils.parseDate(str, "yyyy-MM-dd", "yyyyMMdd");
                        field.set(object, date);
                    } else if (field.getType() == BigDecimal.class) {
                        String str = String.valueOf(map.get(field.getName()));
                        BigDecimal bigDecimal = new BigDecimal(str);
                        field.set(object, bigDecimal);
                    } else {
                        field.set(object, map.get(field.getName()));
                    }
                }
            }
            return object;
        } catch (Exception e) {
            log.error("map转object异常: {}", e.getMessage());
        }
        return null;
    }

    /**
     * object转map
     *
     * @param mapList map列表
     * @param clazz   目标对象class
     * @param <T>     目标对象类型
     * @return 目标对象列表
     */
    public static <T> List<T> mapToBeanList(List<Map<String, Object>> mapList, Class<T> clazz) {
        List<T> list = new ArrayList<>();
        for (Map<String, Object> map : mapList) {
            list.add(mapToBean(map, clazz));
        }
        return list;
    }

    /**
     * 设置空值
     *
     * @param object 源对象
     * @return 空值对象
     */
    public static Object setNullValue(Object object) {
        try {
            Field[] fields = object.getClass().getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                if (field.get(object) == null) {
                    field.set(object, "");
                }
            }
        } catch (Exception e) {
            log.error("object赋值异常: {}", e.getMessage());
        }
        return object;
    }
}
